package com.example.score.score;

import com.example.score.account.Account;
import com.example.score.account.Manager;
import com.example.score.account.Referee;
import com.example.score.entity.Message;
import com.example.score.entity.SportData;
import com.example.score.entity.Sportsman;
import com.example.score.utils.JsonSocketMessageUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Service
public class JudgeSocket extends TextWebSocketHandler {


    SocketScore socketScore;


    @Autowired
    public void setSocketScore(@Lazy SocketScore socketScore) {
        this.socketScore = socketScore;
    }

    @Autowired
    Manager manager;

    Map<Account, WebSocketSession> judgeSessionMap = new ConcurrentHashMap<>(10);

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String judgeId = (String) session.getAttributes().get("judgeId");
        Account account = manager.getJudgeById(judgeId);
        // 重连接之后应该替换原有连接
        if (!judgeSessionMap.containsKey(account)) {
            judgeSessionMap.put(account, session);
        } else {
            log.info("reset session of" + account.getAccount());
            judgeSessionMap.remove(account);
            judgeSessionMap.put(account, session);
        }
        Message currentMessage = socketScore.getCurrentMessage(manager.getRefereeByAccount(account));
        if (currentMessage != null) {
            session.sendMessage(JsonSocketMessageUtil.wrapMessage(currentMessage));
        }
        log.info("裁判：{}连接系统", account.getAccount());
        log.info("当前在场裁判" + Arrays.toString(judgeSessionMap.keySet().toArray()));
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        Message data = JsonSocketMessageUtil.parseMessageToJson(message);
        // 利用反射动态调用方法
        Method method = this.getClass().getDeclaredMethod(data.getAction(), WebSocketSession.class, Message.class);
        method.invoke(this, session, data);
    }

    public void ping(WebSocketSession session, Message message) throws IOException {
        session.sendMessage(JsonSocketMessageUtil.wrapMessage(message));
    }

    /**
     * 裁判提交评分成绩，
     * 将成绩返回给裁判长
     *
     * @param session
     * @param data
     */
    public void commitScore(WebSocketSession session, Message data) {
        // 通过session找到对应的账号，然后找出对应的裁判长
        Set<Map.Entry<Account, WebSocketSession>> entrySet = judgeSessionMap.entrySet();
        Account account = null;
        for (Map.Entry<Account, WebSocketSession> entry : entrySet) {
            if (entry.getValue().equals(session)) {
                account = entry.getKey();
                break;
            }
        }
        // 封装消息
        List<SportData> goalDataList = data.getData();
        for (SportData goalData : goalDataList) {
            goalData.setScore(goalData.getGoal());
            // 找到裁判的直属裁判长
            if (account != null) {
                log.info("裁判{}打分，分数情况{}", account.getAccount(), data);
                // TODO 如果judgeId是场地2的，那么就减去6
                int judgeId = Integer.parseInt(account.getId());
//                if (judgeId > 6) {
//                    goalData.setJudgeId(String.valueOf(judgeId - 6));
//                } else {
                goalData.setJudgeId(account.getId());
//                }
                account = manager.getRefereeByAccount(account);
                socketScore.backScore(account, data);
            } else {
                log.info("通过{}SESSION无法找到裁判, Map{}", session, Arrays.toString(judgeSessionMap.entrySet().toArray()));
            }
        }
    }


    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        Set<Map.Entry<Account, WebSocketSession>> set = judgeSessionMap.entrySet();
        for (Map.Entry<Account, WebSocketSession> entry : set) {
            if (entry.getValue().equals(session)) {
                log.info("裁判：{}退出", entry.getKey());
                judgeSessionMap.remove(entry.getKey());
                break;
            }
        }

        log.info("当前在场裁判" + Arrays.toString(judgeSessionMap.keySet().toArray()));
    }

    public void notifyJudgesScore(List<Account> judges, Message message) throws IOException {
        message.action = "score";
        List<WebSocketSession> sessions = new ArrayList<>(10);
        for (Account judge : judges) {
            WebSocketSession session = null;
            if ((session = judgeSessionMap.get(judge)) != null) {
                sessions.add(session);
            }
        }
        // 通知列表中的裁判进行操作
        for (WebSocketSession session : sessions) {
            session.sendMessage(JsonSocketMessageUtil.wrapMessage(message));
        }
    }

    public void notifyJudgesReScore(List<Account> judges, Message message) throws IOException {
        List<WebSocketSession> sessions = new ArrayList<>(1);
        for (Account judge : judges) {
            WebSocketSession session = null;
            if ((session = judgeSessionMap.get(judge)) != null) {
                sessions.add(session);
            }
        }
        // 通知列表中的裁判进行操作
        for (WebSocketSession session : sessions) {
            session.sendMessage(JsonSocketMessageUtil.wrapMessage(message));
        }
    }

    public void startGroup(List<Sportsman> sportsmen, String hRefereeId) throws IOException {
        Referee referee = manager.getRefereeById(hRefereeId);
        List<Account> judges = referee.getJudges();
        Message message = new Message();
        ArrayList<SportData> datas = new ArrayList<>(sportsmen.size());
        for (Sportsman sportsman : sportsmen) {
            SportData data = new SportData();
            data.setEvent(sportsman.getEvent());
            data.setGoal(String.valueOf(sportsman.getGoal()));
            data.setIndex(String.valueOf(sportsman.getIndex()));
            data.setGroupIndex(String.valueOf(sportsman.getGroupIndex()));
            data.setGroupName(sportsman.getGroupName());
            data.setName(sportsman.getSportName());
            data.setRefereeId(hRefereeId);
            data.setUnit(sportsman.getUnit());
            datas.add(data);
        }
        message.setData(datas);
        notifyJudgesScore(judges, message);
    }
}
